﻿#include "stdafx.h"
#include "skinmenu.h"
#include "PopupMenuSkin.h"

enum { REDRAWALL = -2 };

#ifndef SPI_GETMENUANIMATION
#define SPI_GETMENUANIMATION  0x1002
#endif
struct colorMapping
{
    int nSrcColor;
    int nDestColor;
};

enum
{
    SBFONT_MARLETT,
    SBFONT_TIP,
    SBFONT_MENU,
    SBFONT_MESSAGE,
    SBFONT_CAPTION,
};

// vista+显示问题
//不过我这个问题找到了。并不是定位问题。
//
//codeproject 的方法一部分通过替换系统颜色实现的。
//
//但是在那个位置出现的颜色竟然不是任何 GetSysColor 能得到的颜色。 而是有一些变色。 比如 GetSysColor(COLOR_MENU) 是 0xf0f0f0 。 但是左侧的区域就变成了 0xf1f1f1 。 而如果有radio标记，还会变成 0xf7f7f7 。 可能是 vista+ 的某种特效。
//
//是很奇怪。但是暂时先这样了。
static colorMapping colors[] = 
{
    	//{ COLOR_HIGHLIGHT, COLOR_HIGHLIGHT },
    { COLOR_WINDOWTEXT, COLOR_WINDOWTEXT },
    { COLOR_GRAYTEXT, COLOR_GRAYTEXT },
    { COLOR_HIGHLIGHTTEXT, COLOR_HIGHLIGHTTEXT },
    { COLOR_3DHILIGHT, COLOR_MENU/*COLOR_3DHILIGHT*/ },
    	//{ COLOR_3DDKSHADOW, COLOR_MENU },
    { COLOR_3DSHADOW, COLOR_3DSHADOW },
    { COLOR_3DFACE, COLOR_MENU/*COLOR_3DFACE*/ },
    { COLOR_MENU, COLOR_MENU },
    { 0xf1f1f1, COLOR_MENU},
};

CSkinMenu::CSkinMenu()
: m_pPopupMenuSkin(0)
, m_nSelIndex(-2)
, m_hContextWnd(NULL)
, m_hMenu(NULL)
, m_nSidebarWidth(5)
, m_dwStyle()
, m_bAnimatedMenus(FALSE)
, m_bFirstRedraw(FALSE)
, m_pParentMenu(NULL)
, m_bGradient(TRUE)
, s_pRenderer(this)
, m_bFirstShow(TRUE)
{
    m_dwStyle = SKMS_SIDEBAR | SKMS_FLAT;
    SystemParametersInfo(SPI_GETMENUANIMATION, 0, &m_bAnimatedMenus, 0);
}

BOOL CSkinMenu::LoadSkin()
{
    m_pPopupMenuSkin = GetSkin().GetPopupMenuSkin();
    return  m_pPopupMenuSkin ? TRUE : FALSE;
}

BOOL CSkinMenu::InstallSkin( HWND hWnd )
{
    if (!hWnd || !LoadSkin())
    {
        return FALSE;
    }
    HookWindow((HWND)NULL);
    HookWindow(hWnd);

    return TRUE;
}

LRESULT CSkinMenu::OnWndMsg( UINT msg,WPARAM wp, LPARAM lp )
{
    LRESULT lr = 0;

    switch(msg)
    {
    case WM_NCPAINT:    // 0x0085
        // the very first WM_NCPAINT appears to be responsible for
        // doing any menu animation. since our std OnNcPaint does not
        // deal with animation we must leave it to the default handler.
        // fortunately, the default handler calls WM_PRINT to implement
        // the animation.
        if (!m_bAnimatedMenus || !m_bFirstRedraw)
        {
            CWindowDC dc(GetCWnd());
            OnNcPaint(&dc);
            return 0;
        }
        break;

    case WM_PRINT: 
        {
            lr = Default();
            OnNcPaint(CDC::FromHandle((HDC)wp));
            return lr;
        }
    case WM_PRINTCLIENT:
        OnPrintClient(CDC::FromHandle((HDC)wp), lp);
        return 0;

    case WM_PAINT:
        {
            CPaintDC dc(GetCWnd());
            SendMessage(m_hWnd, WM_PRINTCLIENT, (WPARAM)(HDC)dc, PRF_CLIENT | PRF_CHECKVISIBLE);
            return 0;
        }

        // 貌似没什么用，还是保留了
        case WM_KEYDOWN:

            switch (wp)
            {
            case VK_UP:
            case VK_DOWN:
            case VK_RIGHT:
                // left is much trickier because if the currently selected item
                // has a popup menu then left will close that submenu, and if
                // we prevent the default redrawing then the submenu is not correctly
                // removed from the screen.
                // so we must always do the default drawing and follow it up with our own.
            case VK_LEFT:
                if (!m_hMenu)
                {
                    if (wp != VK_LEFT) 
                        SetRedraw(FALSE);

                    lr = Default();

                    if (wp != VK_LEFT) 
                        SetRedraw(TRUE);

                    //					TRACE ("Invalidating entire menu in response to a cursor keypress\n");

                    m_nSelIndex = -1; // reset current selection because its too risky to 
                    // try to figure it out for ourselves
                    Invalidate(FALSE);
                    UpdateWindow(GetHwnd());

                    m_bFirstRedraw = FALSE;
                }
                else // have menu handle
                {
                    int nPrevSel = GetCurSel();

                    if (wp != VK_LEFT) 
                        SetRedraw(FALSE);

                    lr = Default();

                    if (wp != VK_LEFT) 
                        SetRedraw(TRUE);

                    // if we have the handle of the menu then 
                    // we can do a selective redraw else we must redraw all
                    m_nSelIndex = GetCurSel();

                    if (m_nSelIndex != nPrevSel)
                    {
                        CRect rInvalid;
                        GetInvalidRect(m_nSelIndex, nPrevSel, rInvalid);

                        //						TRACE ("Invalidating menu items %d & %d in response to a cursor keypress\n", m_nSelIndex, nPrevSel);

                        InvalidateRect(GetHwnd(), rInvalid, FALSE);
                        UpdateWindow(GetHwnd());

                        m_bFirstRedraw = FALSE;
                    }
                }
                return lr;
            }
            break;

    case 0x1e5: 

        if (m_nSelIndex != (int)wp)
        {
            // attempt to do a partial redraw where possible
            // this needs more thought
            CRect rInvalid;

            if (m_hMenu)
                GetInvalidRect((int)wp, m_nSelIndex, rInvalid);
            else
                GetClientRect(rInvalid);

            // prevent redrawing during default message processing
            // because otherwise the item is redrawn oven ours.
            SetRedraw(FALSE);
            lr = Default();
            SetRedraw(TRUE);

            m_nSelIndex = (int)wp;

            InvalidateRect(m_hWnd, rInvalid, FALSE);

            if (!m_bFirstRedraw)
                UpdateWindow(m_hWnd);
        }

        // special fix for animated menus
        if (m_bAnimatedMenus && m_bFirstRedraw)
        {
            CWindowDC dc(GetCWnd());
            OnNcPaint(&dc);
        }

        m_bFirstRedraw = FALSE;
        return lr;

    case WM_NCCALCSIZE:
        if (Sidebar())
        {
            lr = Default();

            LPRECT pRect = wp ? &((LPNCCALCSIZE_PARAMS)lp)->rgrc[0] : (LPRECT)lp;
            pRect->left += m_nSidebarWidth;

            return lr;
        }
        break;

    case WM_WINDOWPOSCHANGING:
        {
            WINDOWPOS* pWP = (WINDOWPOS*)lp;

            // adjust width for sidebar
            if (Sidebar() && !(pWP->flags & SWP_NOSIZE))
                pWP->cx += m_nSidebarWidth;

            // if we have a parent menu we may need to adjust our
            // pos to avoid client repainting issues
            if (m_pParentMenu && !(pWP->flags & SWP_NOMOVE))
            {
                // if we are on the right side of our parent
                // then we need to adjust ourselves to avoid the client rect
                CRect rParentWindow;
                ::GetWindowRect(m_pParentMenu->GetHwnd(), rParentWindow);

                if (pWP->x > rParentWindow.left) // right
                {
                    CRect rParentClient;
                    ::GetClientRect(m_pParentMenu->GetHwnd(), rParentClient);

                    m_pParentMenu->ClientToScreen(rParentClient);

                    pWP->x = rParentClient.right;
                }
            }
        }
        break;

    case WM_ERASEBKGND: 
        return TRUE; 
        break;
    default:
        break;
    }
    return CSkinWnd::OnWndMsg(msg, wp, lp);
}

void CSkinMenu::OnPrintClient(CDC* pDC, DWORD dwFlags)
{
    CRect rClient;
    GetClientRect(rClient);

    CRect rClip(rClient);
    //	pDC->GetClipBox(rClip);

    // create standard back buffer and another dc on which 
    // to layer the background and foreground
    CDC dcMem, dcMem2;
    dcMem.CreateCompatibleDC(NULL);
    dcMem2.CreateCompatibleDC(NULL);

    // use screen dc for creating bitmaps because
    // menu dc's seem not to be standard.
    CDC* pDCScrn = CWnd::GetDesktopWindow()->GetDC();

    CBitmap bmMem, bmMem2;
    bmMem.CreateCompatibleBitmap(pDCScrn, rClient.right, rClient.bottom);
    bmMem2.CreateCompatibleBitmap(pDCScrn, rClient.right, rClient.bottom);

    // release screen dc asap
    CWnd::GetDesktopWindow()->ReleaseDC(pDCScrn);

    // prepare dc's
    dcMem.SetBkMode(TRANSPARENT);
    dcMem.SetBkColor(GetSysColor(COLOR_MENU));

    CBitmap* pOldBM = dcMem.SelectObject(&bmMem);
    CFont* pOldFont = dcMem.SelectObject(GetFont(SBFONT_MENU));
    CBitmap* pOldBM2 = dcMem2.SelectObject(&bmMem2);

    // trim clip rgn
    if (rClip.top)
        dcMem.ExcludeClipRect(0, 0, rClient.right, rClip.top);

    if (rClip.bottom < rClient.bottom)
        dcMem.ExcludeClipRect(0, rClip.bottom, rClient.right, rClient.bottom);

    // draw background
    //	dcMem.FillSolidRect(rClient, GetSysColor(COLOR_MENU));
    dcMem.FillSolidRect(rClip, GetSysColor(COLOR_MENU));

    // default draw
    CSubclassWnd::WindowProc(WM_PRINTCLIENT, (WPARAM)(HDC)dcMem, (LPARAM)dwFlags);

    // replace the system colors with skin colors
    CDC* pDCSrc = ReplaceSystemColors(&dcMem, &dcMem2, rClient, rClip);

    // blt the lot to pDC
    //	pDC->BitBlt(0, 0, rClient.right, rClient.bottom, pDCSrc, 0, 0, SRCCOPY);
    pDC->BitBlt(rClip.left, rClip.top, rClip.Width(), rClip.Height(), 
        pDCSrc, rClip.left, rClip.top, SRCCOPY);

    // cleanup
    dcMem.SelectObject(pOldBM);
    dcMem.SelectObject(pOldFont);
    dcMem.DeleteDC();
    bmMem.DeleteObject();

    dcMem2.SelectObject(pOldBM2);
    dcMem2.DeleteDC();
    bmMem2.DeleteObject();
}

// 似乎没有调用啊？
void CSkinMenu::OnPaint(CDC* pDC)
{
    // construct a back buffer for the default draw
    CRect rClient;
    GetClientRect(rClient);

    CBitmap bmMem;
    bmMem.CreateCompatibleBitmap(pDC, rClient.right, rClient.bottom);

    CDC dcMem;
    dcMem.CreateCompatibleDC(NULL);

    dcMem.SetBkMode(TRANSPARENT);
    dcMem.SetBkColor(GetSysColor(COLOR_MENU));
    CBitmap* pOldBM = dcMem.SelectObject(&bmMem);
    CFont* pOldFont = dcMem.SelectObject(GetFont(SBFONT_MENU));

    // draw background
    dcMem.FillSolidRect(rClient, GetSysColor(COLOR_MENU));

    // default draw
    CSubclassWnd::WindowProc( WM_PAINT, (WPARAM)(HDC)dcMem, 0);

    // create another dc on which to layer the background and foreground
    CDC dcMem2;
    dcMem2.CreateCompatibleDC(NULL);

    CBitmap bmMem2;
    bmMem2.CreateCompatibleBitmap(pDC, rClient.right, rClient.bottom);
    CBitmap* pOldBM2 = dcMem2.SelectObject(&bmMem2);

    // replace the system colors with skin colors
    CDC* pDCSrc = ReplaceSystemColors(&dcMem, &dcMem2, rClient, NULL);

    // blt the lot to pDC
    pDC->BitBlt(0, 0, rClient.right, rClient.bottom, pDCSrc, 0, 0, SRCCOPY);

    // cleanup
    dcMem.SelectObject(pOldBM);
    dcMem.SelectObject(pOldFont);
    dcMem.DeleteDC();
    bmMem.DeleteObject();

    dcMem2.SelectObject(pOldBM2);
    dcMem2.DeleteDC();
    bmMem2.DeleteObject();
}

void CSkinMenu::OnNcPaint(CDC* pDC)
{
    //int nOS = CSkinBase::GetOS();
    BOOL bIRender = TRUE;
    //BOOL bIRender = !(nOS == SBOS_95 || nOS == SBOS_NT4);

    CRect rWindow, rClient;
    GetDrawRect(rWindow, rClient);

    CRect rClip;
    pDC->GetClipBox(rClip);

    //	TRACE ("CSkinMenu::OnNcPaint(clip box = {%d x %d})\n", rClip.Width(), rClip.Height());

    // back buffer
    CBitmap bmMem;
    bmMem.CreateCompatibleBitmap(pDC, rWindow.right, rWindow.bottom);

    CDC dcMem;
    dcMem.CreateCompatibleDC(NULL);

    CBitmap* pOldBM = dcMem.SelectObject(&bmMem);

    COLORREF crColorMenu = GetColor(COLOR_MENU);

    // draw sidebar
    CRect rSidebar(rWindow);
    rSidebar.DeflateRect(3, 3);
    rSidebar.right = rSidebar.left + m_nSidebarWidth;

    if (Sidebar() && (!s_pRenderer || !s_pRenderer->DrawMenuSidebar(&dcMem, rSidebar)))
    {
        dcMem.FillSolidRect(rSidebar, VaryColor(GetColor(COLOR_3DSHADOW), 0.9f));
    }

    // then clip sidebar out
    dcMem.ExcludeClipRect(rSidebar);

    // draw nc bkgnd
    // note: do this ourselves under win95 to ensure continuity with client bkgnd
    if (!bIRender || !s_pRenderer || !s_pRenderer->DrawMenuNonClientBkgnd(&dcMem, rWindow))
    {
        dcMem.FillSolidRect(rWindow, crColorMenu);
    }

    // draw nc border
    if (!bIRender || !s_pRenderer || !s_pRenderer->DrawMenuBorder(&dcMem, rWindow))
    {
        COLORREF crShadow = GetColor(COLOR_3DSHADOW);

        if (Flat())
            dcMem.Draw3dRect(rWindow, crShadow, crShadow);
        else
            dcMem.Draw3dRect(rWindow, GetColor(COLOR_3DHIGHLIGHT), crShadow);
    }

    // blt to screen
    int nSaveDC = pDC->SaveDC(); // must restore dc to original state

    pDC->ExcludeClipRect(rClient);
    pDC->BitBlt(0, 0, rWindow.right, rWindow.bottom, &dcMem, 0, 0, SRCCOPY);

    pDC->RestoreDC(nSaveDC);

    // cleanup
    dcMem.SelectObject(pOldBM);
}
enum
{
    COLOR_PARENTBKGND = 0x00ff,

    COLOR_LAST,
};
COLORREF CSkinMenu::GetColor(int nColor)
{
    if (nColor == COLOR_PARENTBKGND)
        nColor = COLOR_3DFACE;

    if (0xf1f1f1 == nColor)
    {
        return RGB(0xf1, 0xf1, 0xf1);
    }
    if (COLOR_WINDOWTEXT == nColor)
        return m_pPopupMenuSkin->m_itemPopup.textNormal;
    else if (COLOR_GRAYTEXT == nColor)
        return m_pPopupMenuSkin->m_itemPopup.textGrayed;
    else if (COLOR_HIGHLIGHTTEXT == nColor)
        return m_pPopupMenuSkin->m_itemPopup.textActive;
    else if (nColor == COLOR_MENU)
        return m_pPopupMenuSkin->m_colorBackground;
    else if (nColor == COLOR_3DSHADOW)
        return m_pPopupMenuSkin->m_itemPopup.colorSeparator;

    //if (nColor == COLOR_WINDOWTEXT)
    //{
    //    return RGB(110, 90, 25);
    //}
    //if (nColor == COLOR_HIGHLIGHTTEXT)
    //{
    //    return RGB(255, 255, 255);
    //}

    return ::GetSysColor(nColor);
}

CFont* CSkinMenu::GetFont(int nFont)
{

    if (nFont == SBFONT_MENU)
    {
        return GetMenuFont();
    }
    return NULL;

}

int CSkinMenu::GetCurSel()
{
    ASSERT (m_hMenu);
    int nItem = GetMenuItemCount(m_hMenu);

    while (nItem--)
    {
        if (GetMenuState(m_hMenu, nItem, MF_BYPOSITION) & MF_HILITE)
            return nItem;
    }

    return -1; // nothing selected
}

void CSkinMenu::GetInvalidRect(int nCurSel, int nPrevSel, LPRECT lpRect)
{
    ASSERT (lpRect);

    if (!m_hMenu || nCurSel == REDRAWALL || nPrevSel == REDRAWALL)
        GetClientRect(lpRect);

    else if (m_hMenu)
    {
        ::SetRectEmpty(lpRect);

        if (nCurSel >= 0 || nPrevSel >= 0)
        {
            if (nCurSel >= 0)
            {
                GetMenuItemRect(NULL, m_hMenu, nCurSel, lpRect);
            }

            if (nPrevSel >= 0)
            {
                CRect rTemp;

                GetMenuItemRect(NULL, m_hMenu, nPrevSel, rTemp);
                ::UnionRect(lpRect, lpRect, rTemp);
            }

            // convert to client coords
            ScreenToClient(lpRect);
        }
    }
}void CSkinMenu::GetDrawRect(LPRECT pWindow, LPRECT pClient)
{
    CRect rWindow;
    GetWindowRect(rWindow);

    if (pClient)
    {
        GetClientRect(pClient);
        ClientToScreen(pClient);
        ::OffsetRect(pClient, -rWindow.left, -rWindow.top);
    }

    if (pWindow)
    {
        rWindow.OffsetRect(-rWindow.TopLeft());
        *pWindow = rWindow;
    }
}
CDC* CSkinMenu::ReplaceSystemColors(CDC* pDCSrc, CDC* pDCDest, LPRECT pRect, LPRECT pClip)
{
    //int nOS = CSkinBase::GetOS();

    //if (nOS == SBOS_95 || nOS == SBOS_NT4)
    //    return pDCSrc;

    // replace the system colors with skin colors
    CMap<COLORREF, COLORREF, int, int&> mapColors;

    // 1. replace the actual background color with COLOR_MENU
    const COLORREF COLORMENU = GetColor(COLOR_MENU);

    COLORREF crSrc, crDest = COLORMENU;

    if (m_nSelIndex != 0)
        crSrc = pDCSrc->GetPixel(pRect->right, pRect->top);
    else
        crSrc = pDCSrc->GetPixel(pRect->right, pRect->bottom);

    // see if user wants to render bkgnd
    if (crSrc != -1)
    {
        if (s_pRenderer && s_pRenderer->DrawMenuClientBkgnd(pDCDest, pRect, pClip))
        {
            //transparent blt
            MyBitBlt(pDCDest, pRect->left, pRect->top, 
                pRect->right - pRect->left, 
                pRect->bottom - pRect->top, pDCSrc, 0, 0, SRCCOPY, crSrc);

            //swap dest and src
            SwapDCs(pDCSrc, pDCDest);
        }
        // else simple color replacement
        else if (ReplaceColor(pDCSrc, crSrc, pDCDest, crDest, pRect, pClip))
        {		
            // swap dest and src
            SwapDCs(pDCSrc, pDCDest);
        }

        mapColors[crSrc] = 1;
    }

    // 2. replace other mapped colors 
    int nColor = sizeof(colors) / sizeof(colorMapping);

    // vista以上图标那栏显示有问题,这里直接获取那栏的像素点颜色
    // 能够在绝大部分情况下显示正常，所以并不完美
    while (nColor--)
    {
        int nTemp;
        if (m_bFirstShow) // 第一次显示时保存非客户
        {
            int x = GetSystemMetrics(SM_CXMENUCHECK) + 11 ; // 9-12
            m_btnColor = GetPixel(pDCSrc->m_hDC, x, 1);
            m_bFirstShow = false;
        }


        if (colors[nColor].nSrcColor == 0xf1f1f1)
        {
            crSrc = IsVista() ? m_btnColor : GetSysColor(COLOR_MENU);
        }
        else
        {
            crSrc = GetSysColor(colors[nColor].nSrcColor);
        }

        if (mapColors.Lookup(crSrc, nTemp))
        {
            //			TRACE("CSkinMenu::ReplaceSystemColors - %d already replaced\n", crSrc);
            continue;
        }
        crDest = GetColor(colors[nColor].nDestColor);

        // if the dest color is COLORMENU let the user have first go
        if (crDest == COLORMENU && s_pRenderer && s_pRenderer->DrawMenuClientBkgnd(pDCDest, pRect, pClip))
        {
            // transparent blt
            MyBitBlt(pDCDest, pRect->left, pRect->top, 
                pRect->right - pRect->left, 
                pRect->bottom - pRect->top, pDCSrc, 0, 0, SRCCOPY, crSrc);

            // swap dest and src
            SwapDCs(pDCSrc, pDCDest);
        }
        // else simple color replacement
        else if (ReplaceColor(pDCSrc, crSrc, pDCDest, crDest, pRect, pClip))
        {		
            // swap dest and src
            SwapDCs(pDCSrc, pDCDest);
        }
        //else
            //TRACE("CSkinMenu::ReplaceSystemColors - GetSysColor(%d) == CSkinBase::GetColor(%d)\n", nSrcColor, nDestColor);

        mapColors[crSrc] = 1;
    }

    return pDCSrc;
}

BOOL CSkinMenu::ReplaceColor(CDC* pDCSrc, COLORREF crSrc, CDC* pDCDest, COLORREF crDest, LPRECT pRect, LPRECT pClip)
{
    if (crSrc == crDest)
        return FALSE;

    // else
    if (pClip)
    {
        pDCDest->FillSolidRect(pClip, crDest);
        MyBitBlt(pDCDest, pClip->left, pClip->top, pClip->right - pClip->left, 
            pClip->bottom - pClip->top, pDCSrc, pClip->left, pClip->top, SRCCOPY, crSrc);
    }
    else
    {
        pDCDest->FillSolidRect(pRect, crDest);
        MyBitBlt(pDCDest, pRect->left, pRect->top, pRect->right - pRect->left, 
            pRect->bottom - pRect->top, pDCSrc, pRect->left, pRect->top, SRCCOPY, crSrc);
    }

    return TRUE;
}
BOOL  CSkinMenu::MyBitBlt(CDC* pDCDest, 
                          int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,
                          CDC* pDCSrc, int nXOriginSrc, int nYOriginSrc, 
                          UINT uROP,	   
                          COLORREF crTransparent)
{
    if (crTransparent != (COLORREF)-1)
    {
        return TransparentBlt(pDCDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
            pDCSrc, nXOriginSrc, nYOriginSrc, nWidthDest, nHeightDest, crTransparent);
    }
    else
    {
        return pDCDest->BitBlt(nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
            pDCSrc, nXOriginSrc, nYOriginSrc, uROP);
    }

    return TRUE;
}
BOOL CSkinMenu::CreateFont(CFont* pFont, LOGFONT* pLF)
{
    ASSERT (pFont && pLF);

    if (!(pFont && pLF))
        return FALSE;

    // override ANSI_CHARSET because some fonts do not show with it
    if (pLF->lfCharSet == ANSI_CHARSET)
        pLF->lfCharSet = DEFAULT_CHARSET;

    pLF->lfQuality = ANTIALIASED_QUALITY;
    pLF->lfOutPrecision = OUT_TT_PRECIS;

    //if (!m_sFontName.IsEmpty())
    //    lstrcpy(pLF->lfFaceName, m_sFontName);

    int m_nFontPointSize = 0, m_nFontHeight = 0;
    if (m_nFontPointSize > 0)
    {
        HDC hDC = GetDC(NULL);
        pLF->lfHeight = -MulDiv(m_nFontPointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
        ReleaseDC(NULL, hDC);
    }
    else if (m_nFontHeight > 0)
        pLF->lfHeight = -m_nFontHeight;

    return pFont->CreateFont(pLF->lfHeight,0,0,0,pLF->lfWeight,FALSE, FALSE, FALSE,DEFAULT_CHARSET ,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH, pLF->lfFaceName);
}
CFont* CSkinMenu::GetMenuFont()
{
    if (!m_fontMenu.GetSafeHandle())
    {
        NONCLIENTMETRICS ncm;
        ZeroMemory(&ncm,sizeof(ncm));
        ncm.cbSize = sizeof(ncm);

        // Get the system font
        SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, (PVOID)&ncm, FALSE);

        VERIFY(CreateFont(&m_fontMenu, &ncm.lfMenuFont));
    }

    return &m_fontMenu;
}

CSkinMenu::~CSkinMenu()
{
    m_fontMenu.DeleteObject();
}

COLORREF CSkinMenu::VaryColor(COLORREF crColor, float fFactor)
{
    BYTE btRed = (BYTE)min(255, (int)(GetRValue(crColor) * fFactor));
    BYTE btGreen = (BYTE)min(255, (int)(GetGValue(crColor) * fFactor));
    BYTE btBlue = (BYTE)min(255, (int)(GetBValue(crColor) * fFactor));

    return RGB(btRed, btGreen, btBlue);
}
BOOL CSkinMenu::TransparentBlt(CDC* pDCDest, 
                               int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,
                               CDC* pDCSrc, 
                               int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, 	
                               UINT crTransparent)
{
    if (nWidthDest < 1) 
        return FALSE;

    if (nWidthSrc < 1) 
        return FALSE; 

    if (nHeightDest < 1) 
        return FALSE;

    if (nHeightSrc < 1) 
        return FALSE;

    if (TransparentBltFast(pDCDest, 
        nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
        pDCSrc, 
        nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,	 
        crTransparent))
        return TRUE;

    // else 
    return TransparentBltSlow(pDCDest, 
        nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
        pDCSrc, 
        nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,	 
        crTransparent);
}
BOOL CSkinMenu::TransparentBltFast(CDC* pDCDest, 		
                                   int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,	
                                   CDC* pDCSrc, 		 
                                   int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,	 
                                   UINT crTransparent) 
{
    //if (!SupportsFastTransparentBlt() || !s_pfnFastTransparentBlt)
    //    return FALSE;

    return ::TransparentBlt(*pDCDest, 
        nXOriginDest, 
        nYOriginDest, 
        nWidthDest, 
        nHeightDest,
        *pDCSrc, 
        nXOriginSrc, 
        nYOriginSrc, 
        nWidthSrc, 
        nHeightSrc,	   
        crTransparent);
}

BOOL CSkinMenu::TransparentBltSlow(CDC* pDCDest, 		
                                   int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,	
                                   CDC* pDCSrc, 		 
                                   int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,	 
                                   UINT crTransparent) 
{
    CDC dcMem, dcMask;

    dcMask.CreateCompatibleDC(pDCDest);
    dcMem.CreateCompatibleDC(pDCDest);

    CBitmap bmMask, bmMem;

    // copy src bitmap to mem dc
    bmMem.CreateCompatibleBitmap(pDCDest, nWidthSrc, nHeightSrc);

    CBitmap* pOldBMMem = dcMem.SelectObject(&bmMem);
    dcMem.BitBlt(0, 0, nWidthSrc, nHeightSrc, pDCSrc, nXOriginSrc, nYOriginSrc, SRCCOPY);

    //	ShowDC(dcMem);

    // Create monochrome bitmap for the mask
    bmMask.CreateBitmap(nWidthSrc, nHeightSrc, 1, 1, NULL);
    CBitmap* pOldBMMask = dcMask.SelectObject(&bmMask);
    dcMem.SetBkColor(crTransparent);

    // Create the mask from the memory DC
    dcMask.BitBlt(0, 0, nWidthSrc, nHeightSrc, &dcMem, 0, 0, SRCCOPY);

    //	ShowDC(dcMask);

    // Set the background in dcMem to black. Using SRCPAINT with black
    // and any other color results in the other color, thus making
    // black the transparent color
    dcMem.SetBkColor(RGB(0,0,0));
    dcMem.SetTextColor(RGB(255,255,255));
    dcMem.BitBlt(0, 0, nWidthSrc, nHeightSrc, &dcMask, 0, 0, SRCAND);

    //	ShowDC(dcMem);

    // Set the foreground to black. See comment above.
    //	pDCDest->SetStretchBltMode(COLORONCOLOR);
    pDCDest->SetStretchBltMode(HALFTONE);
    pDCDest->SetBkColor(RGB(255,255,255));
    pDCDest->SetTextColor(RGB(0,0,0));

    //	CPoint ptOrg;
    //	::GetBrushOrgEx(*pDCDest, &ptOrg);
    //	::SetBrushOrgEx(*pDCDest, 0, 0, &ptOrg);

    if (nWidthDest == nWidthSrc && nHeightDest == nHeightSrc)
    {
        pDCDest->BitBlt(nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, &dcMask, 0, 0, SRCAND);
        //		ShowDC(*pDCDest);
    }
    else
    {
        pDCDest->StretchBlt(nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, 
            &dcMask, 0, 0, nWidthSrc, nHeightSrc, SRCAND);
        //		ShowDC(*pDCDest);
    }

    // Combine the foreground with the background
    if (nWidthDest == nWidthSrc && nHeightDest == nHeightSrc)
    {
        pDCDest->BitBlt(nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, &dcMem, 0, 0, SRCPAINT);
        //		ShowDC(*pDCDest);
    }
    else
    {
        pDCDest->StretchBlt(nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, 
            &dcMem, 0, 0, nWidthSrc, nHeightSrc, SRCPAINT);
        //		ShowDC(*pDCDest);
    }

    dcMask.SelectObject(pOldBMMask);
    dcMem.SelectObject(pOldBMMem);

    return TRUE;
}

BOOL CSkinMenu::DrawMenuNonClientBkgnd(CDC* pDC, LPRECT pRect)
{
    if (m_bGradient)
    {
        DrawGradientBkgnd(pDC, pRect, NULL);
        return TRUE;
    }
    else
        return FALSE;
}

BOOL CSkinMenu::DrawMenuClientBkgnd(CDC* pDC, LPRECT pRect, LPRECT pClip)
{
    if (m_bGradient)
    {
        DrawGradientBkgnd(pDC, pRect, pClip);
        return TRUE;
    }
    else
        return FALSE;
}

void CSkinMenu::DrawGradientBkgnd(CDC* pDC, LPRECT pRect, LPRECT pClip)
{
    COLORREF crFrom = GetColor(COLOR_MENU);
    COLORREF crTo = RGB(255, 255, 255);

    if (pClip)
    {
        // ensure that pClip is at least 100 pixels high else the 
        // gradient has artifacts
        CRect rClip(pClip), rRect(pRect);

        if (rClip.Height() < 100 && rRect.Height() > 100)
        {
            rClip.InflateRect(0, (min(rRect.Height(), 100) - rClip.Height()) / 2);

            if (rClip.top < rRect.top)
                rClip.OffsetRect(0, rRect.top - rClip.top);

            else if (rClip.bottom > rRect.bottom)
                rClip.OffsetRect(0, rRect.bottom - rClip.bottom);
        }

        float fHeight = (float)rRect.Height();

        float fFromFactor = (pRect->bottom - rClip.top) / fHeight;
        float fToFactor = (pRect->bottom - rClip.bottom) / fHeight;

        crFrom = BlendColors(crFrom, crTo, fFromFactor);
        crTo = BlendColors(crFrom, crTo, fToFactor);

        GradientFill(pDC, rClip, crFrom, crTo, FALSE);
    }
    else
        GradientFill(pDC, pRect, crFrom, crTo, FALSE);
}
BOOL CSkinMenu::GradientFill(CDC* pDCDest, LPRECT lpRect, COLORREF crFrom, COLORREF crTo, BOOL bHorz)
{
    if (!lpRect)
        return FALSE;

    if (::IsRectEmpty(lpRect))
        return FALSE;

    if (crFrom == crTo)
    {
        pDCDest->FillSolidRect(lpRect, crFrom);
        return TRUE;
    }

    if (GradientFillFast(pDCDest, lpRect, crFrom, crTo, bHorz))
        return TRUE;

    // else
    return GradientFillSlow(pDCDest, lpRect, crFrom, crTo, bHorz);
}
BOOL CSkinMenu::GradientFillFast(CDC* pDCDest, LPRECT lpRect, COLORREF crFrom, COLORREF crTo, BOOL bHorz)
{

    TRIVERTEX vert[2];
    vert[0].x      = lpRect->left;
    vert[0].y      = lpRect->top;
    vert[0].Red    = GetRValue(crFrom) << 8;
    vert[0].Green  = GetGValue(crFrom) << 8;
    vert[0].Blue   = GetBValue(crFrom) << 8;
    vert[0].Alpha  = 0x0000;

    vert[1].x      = lpRect->right;
    vert[1].y      = lpRect->bottom; 
    vert[1].Red    = GetRValue(crTo) << 8;
    vert[1].Green  = GetGValue(crTo) << 8;
    vert[1].Blue   = GetBValue(crTo) << 8;
    vert[1].Alpha  = 0x0000;

    GRADIENT_RECT gRect = { 0, 1 };

    return ::GradientFill(*pDCDest, 
        vert,
        2,
        &gRect,
        1,
        bHorz ? GRADIENT_FILL_RECT_H : GRADIENT_FILL_RECT_V);
}
BOOL CSkinMenu::GradientFillSlow(CDC* pDCDest, LPRECT lpRect, COLORREF crFrom, COLORREF crTo, BOOL bHorz)
{
    if (!pDCDest || !lpRect)
        return FALSE;

    int nWidth = lpRect->right - lpRect->left;
    int nHeight = lpRect->bottom - lpRect->top;

    if (bHorz)
    {
        for (int nX = lpRect->left; nX < lpRect->right; nX++)
            pDCDest->FillSolidRect(nX, lpRect->top, 1, nHeight, BlendColors(crFrom, crTo, (lpRect->right - nX) / (float)nWidth));
    }
    else
    {
        for (int nY = lpRect->top; nY < lpRect->bottom; nY++)
            pDCDest->FillSolidRect(lpRect->left, nY, nWidth, 1, BlendColors(crFrom, crTo, (lpRect->bottom - nY) / (float)nHeight));
    }

    return TRUE;
}
COLORREF CSkinMenu::BlendColors(COLORREF crA, COLORREF crB, float fAmountA)
{
    BYTE btRed = (BYTE)min(255, (int)(GetRValue(crA) * fAmountA + GetRValue(crB) * (1.0f - fAmountA)));
    BYTE btGreen = (BYTE)min(255, (int)(GetGValue(crA) * fAmountA + GetGValue(crB) * (1.0f - fAmountA)));
    BYTE btBlue = (BYTE)min(255, (int)(GetBValue(crA) * fAmountA + GetBValue(crB) * (1.0f - fAmountA)));

    return RGB(btRed, btGreen, btBlue);
}
BOOL CSkinMenu::DrawMenuSidebar(CDC* pDC, LPRECT pRect, LPCTSTR szTitle)
{
    if (m_bGradient)
    {
        COLORREF crColor = GetColor(COLOR_3DSHADOW);

        COLORREF crFrom = crColor;
        COLORREF crTo = VaryColor(crFrom, 2.0f);

        GradientFill(pDC, pRect, crFrom, crTo, FALSE);
        return TRUE;
    }
    else 
    {
        COLORREF crColor = GetColor(COLOR_3DSHADOW);

        pDC->FillSolidRect(pRect, crColor);
        return TRUE;
    }

    return FALSE;
}

BOOL CSkinMenu::DrawMenuBorder(CDC* pDC, LPRECT pRect)
{
    return FALSE;
}
